<#include "license.ftl">
<@license/>
package ${doc.all.package}.service;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.logging.Logger;
import java.util.concurrent.ConcurrentLinkedQueue;
import redora.exceptions.ConnectException;
import redora.exceptions.QueryException;

import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.FINE;

public class ServiceFactory {
    static final transient Logger l = Logger.getLogger("${doc.all.package}.service.ServiceFactory");

    static final long MAX_IDLE_TIME = 60*1000*10L;

    static {
        try {
            l.log(INFO, "Loading mysql JDBC driver");
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            l.log(SEVERE, "Is the JDBC driver in you path? (should be if you use Maven)", e);
        }
    }

    private ServiceFactory() {    //avoid instantiation
    }

    static long invocations = 0;

<#list doc["/all/object/@name"] as modelname>
    final static ConcurrentLinkedQueue<${modelname}Service> ${modelname?uncap_first} = new ConcurrentLinkedQueue<${modelname}Service>();
    final static ConcurrentLinkedQueue<${modelname}ServiceJSON> ${modelname?uncap_first}JSON = new ConcurrentLinkedQueue<${modelname}ServiceJSON>();
</#list>

<#list doc["/all/object"] as object>
    <#assign modelname = object.@name>
    public static ${modelname}Service ${modelname?uncap_first}Service() throws ConnectException {
        ${modelname}Service retVal = ${modelname?uncap_first}.poll();
        if (retVal == null) {
            retVal = new ${modelname}Service();

            l.log(FINE, "Opening ${modelname}");
            if (invocations++ == 0) {
                upgrade();
            }
        } else if (System.currentTimeMillis() - retVal.last > MAX_IDLE_TIME) {
            l.log(INFO, "Throwing away old ${modelname} service");
            retVal.close();
            retVal = ${modelname?uncap_first}Service();
        }

        return retVal;
    }
 
    public static void close(@Nullable ${modelname}Service service) {
        if (service != null)
            ${modelname?uncap_first}.offer(service);
    }

    @NotNull
    public static ${modelname}ServiceJSON ${modelname?uncap_first}ServiceJSON() throws ConnectException {
        ${modelname}ServiceJSON retVal = ${modelname?uncap_first}JSON.poll();
        if (retVal == null) {
            retVal = new ${modelname}ServiceJSON();
            l.log(FINE, "Opening ${modelname}JSON");
            if (invocations++ == 0) {
                upgrade();
            }
        } else if (System.currentTimeMillis() - retVal.last > MAX_IDLE_TIME) {
            l.log(INFO, "Throwing away old json ${modelname} service");
            retVal.close();
            retVal = ${modelname?uncap_first}ServiceJSON();
        }

        return retVal;
    }

    public static void close(@Nullable ${modelname}ServiceJSON service) {
        if (service != null)
            ${modelname?uncap_first}JSON.offer(service);
    }

</#list>

    /**
    * Runs Upgrade. This should only be done once at system startup.
    * @see Upgrade
    * @throws ConnectException On failing to perform the upgrade queries
    */
    static void upgrade() throws ConnectException {
        Upgrade up = new Upgrade();
        try {
            up.create(null, true);
        } catch (QueryException e) {
            throw new ConnectException("Can't init ${modelname} service, failed to check database status", e);
        } finally {
            up.close();
        }
    }
}